import pygtk
pygtk.require('2.0')

import gtk
import gtk.gdk as gdk
import gobject
import pango
import time
import gc
import possumlib.utils
import possumlib.geometry
import os
import os.path
import math
import random
import cairo
import pangocairo

# *********************************************************************
class CairoFX(object):
	
	# -------------------------------------------------------------------
	def __init__(self, ctx):
		self.ctx	=	ctx
	
	# -------------------------------------------------------------------
	@classmethod
	def randombound(self, lower, upper):
		return random.random() * (upper - lower) + lower
	
	# -------------------------------------------------------------------
	@classmethod
	def bcolor(self):
		return (	self.randombound(0.2, 1),
							self.randombound(0.2, 1),
							self.randombound(0.2, 1),
					)
					
	# -------------------------------------------------------------------
	@classmethod
	def bcolor3(self):
		r = self.bcolor()
		return (	r,
							self.bcolor(),
							r,
					)
					
	# -------------------------------------------------------------------
	@classmethod
	def bcolor2(self):
		b = self.bcolor()
		e = self.bcolor()
		return (	b,
							( (b[0] + e[0]) / 2, (b[1] + e[1]) / 2, (b[2] + e[2]) / 2),
							e,
					)
					
	# -------------------------------------------------------------------
	def RoundedRect(self, x, y, w, h, r = 10):
		ctx = self.ctx
		ctx.move_to(x + r, y)                      
		ctx.line_to(x + w - r, y)                    
		ctx.curve_to(x + w, y, x + w, y, x + w, y + r)       
		ctx.line_to(x + w, y + h - r)                  
		ctx.curve_to(x + w, y + h, x + w, y + h, x + w - r, y + h) 
		ctx.line_to(x + r, y + h)                    
		ctx.curve_to(x, y + h, x, y + h, x, y + h - r)       
		ctx.line_to(x, y + r)                      
		ctx.curve_to(x, y, x, y, x + r, y)   
		ctx.fill()

# *********************************************************************
class Notebook(gtk.DrawingArea):
	
	# -------------------------------------------------------------------
	def __init__(self, id = None, text = None):
		gtk.DrawingArea.__init__(self)
		
		self.Clear()
		self.id		= id
		self.text	= text
		
	# -------------------------------------------------------------------
	def Clear(self):
		self.text				=	None
		self.textcolor	=	(0, 0, 0)
		self.backgroundcolor	=	CairoFX.bcolor()
		self.scaletext				=	True
		self.fontsize					=	10
		
		c = self.textcolor
		
		self.clicktextcolor	=	(1 - c[0], 1 - c[1], 1 - c[2])
		
		c = self.backgroundcolor
		self.clickbackgroundcolor = (1 - c[0], 1 - c[1], 1 - c[2])
		
		self.status			=	self.NORMAL
		self.gradient		=	None	#bcolor2()

# *********************************************************************
class Button(gtk.DrawingArea):
	
	(	NORMAL,
		DISABLED,
		HIGHLIGHTED,
		CLICKED,
		TOGGLED,
	) = range(1, 6)	
	
	# -------------------------------------------------------------------
	def __init__(self, id = None, text = None):
		gtk.DrawingArea.__init__(self)
		
		self.Clear()
		self.id		= id
		self.text	= text
				
		self.set_events(gtk.gdk.EXPOSURE_MASK
			| gtk.gdk.BUTTON_PRESS_MASK
			| gtk.gdk.BUTTON_RELEASE_MASK)
			
		self.connect("expose_event", self.OnExpose)
		self.connect("button_release_event", self.OnRelease)
		
	# -------------------------------------------------------------------
	def Clear(self):
		self.text				=	None
		self.textcolor	=	(0, 0, 0)
		self.backgroundcolor	=	CairoFX.bcolor()
		self.scaletext				=	True
		self.fontsize					=	10
		
		c = self.textcolor
		
		self.clicktextcolor	=	(1 - c[0], 1 - c[1], 1 - c[2])
		
		c = self.backgroundcolor
		self.clickbackgroundcolor = (1 - c[0], 1 - c[1], 1 - c[2])
		
		self.status			=	self.NORMAL
		self.gradient		=	None	#bcolor2()
		
	# -------------------------------------------------------------------
	def OnExpose(self, widget, event):
		wnd = self.window
		dw, dh = wnd.get_size()
		
		if dw <= 0 or dh <= 0:
			return
		
		ctx = wnd.cairo_create()
		ctx.set_source_rgb(0, 0, 0)
		ctx.rectangle(0, 0, dw, dh)
		ctx.clip_preserve()
		ctx.fill()
		
		if self.status == self.CLICKED:
			r, g, b = self.clickbackgroundcolor
		else:
			r, g, b = self.backgroundcolor
			
		ctx.set_source_rgb(r, g, b)
		
		if self.gradient:
			lg = cairo.LinearGradient(0, 0, 0, dh)
			
			r, g, b = self.gradient[0]
			lg.add_color_stop_rgb(0, r, g, b)
			
			r, g, b = self.gradient[1]
			lg.add_color_stop_rgb(0.5, r, g, b)
			
			r, g, b = self.gradient[2]
			lg.add_color_stop_rgb(1, r, g, b)
			
			ctx.set_source(lg)
		
		radius = dh / 2
		rectwidth = dw - dh / 2
		
		ctx.arc(radius, radius, radius, math.pi / 2, math.pi * 3 / 2)		
		ctx.line_to(radius + rectwidth, 0)
		ctx.arc(dw - radius, radius, radius, math.pi * 3 / 2, math.pi / 2)
		ctx.line_to(radius, dh)
		ctx.fill()
		
		if self.text:
			if self.status == self.CLICKED:
				r, g, b = self.clicktextcolor
			else:
				r, g, b = self.textcolor
			
			ctx.set_source_rgb(r, g, b)
			
			layout = ctx.create_layout()
			layout.set_text(self.text)
			layout.set_alignment(pango.ALIGN_CENTER)
			
			if self.scaletext:
				textsize = int(dh * 0.5)
			
				layout.set_font_description( pango.FontDescription('sans bold %s' % textsize) )
				lw, lh = layout.get_pixel_size()
			
				textratio = float(lw) / lh
				buttonratio = float(dw) / dh			
			
				if textratio >= buttonratio:
					newratio = float(dw) / lw * 0.8
					layout.set_font_description( 
						pango.FontDescription('sans bold %s' % int(textsize * newratio) )
					)
				else:
					newratio = float(dh) / lh * 0.8
					layout.set_font_description( 
						pango.FontDescription('sans bold %s' % int(textsize * newratio) )
					)
			else:
				layout.set_font_description( 
					pango.FontDescription('sans bold %s' % self.fontsize )
				)
				
			lw, lh = layout.get_pixel_size()
			
			ctx.move_to(dw / 2 - lw / 2, dh / 2 - lh / 2)		
			ctx.show_layout(layout)			
			
	# -------------------------------------------------------------------
	def OnRelease(self, widget, event):
		if event.button == 1:
			self.status	=	self.CLICKED
			self.emit('lclick', self.id)
			self.queue_draw()
			gobject.timeout_add(500, self.ClickTimeout)
			
		return True
		
	# -------------------------------------------------------------------
	def ClickTimeout(self):
		self.status = self.NORMAL
		self.queue_draw()

# *********************************************************************
gobject.signal_new("lclick", 
	Button,
	gobject.SIGNAL_ACTION,
	gobject.TYPE_NONE,
	(gobject.TYPE_PYOBJECT,) )

# *********************************************************************
class BaseStyle(object):
	data	=	{
		'color'						:	(0, 0, 0),
		'backgroundcolor'	:	(0, 0, 1),
	}
	
	# -------------------------------------------------------------------
	def __getitem__(self, k):
		if self.data.has_key(k):
			return self.data[k]
		
		return None

# *********************************************************************
class Style(object):
	
	# -------------------------------------------------------------------
	def __init__(self, parent = BaseStyle):
		self.data 	= {}
		self.parent	=	parent
		
	# -------------------------------------------------------------------
	def __getitem__(self, k):
		if self.data.has_key(k):
			return self.data[k]
		
		return self.parent[k]
		
	# -------------------------------------------------------------------
	def __setitem__(self, k, v):
		self.data[k] = v
		
	# -------------------------------------------------------------------
	def __delitem__(self, k, v):
		self.data[k] = v

# *********************************************************************
class StyleSheet(object):
	
	# -------------------------------------------------------------------
	def __init__(self):
		self.styles	=	{}
	


# *********************************************************************
class Element(object):
	
	# -------------------------------------------------------------------
	def __init__(self, parent, id = None, cls = None):
		self.parent	=	parent
		self.id 		= id
		self.cls		=	cls
		self.rect 	= possumlib.geometry.Rect()

# *********************************************************************
# Trek-like Lcars drawing area
# 
# In order to handle the animation for switching button presses, this
# class handles the drawing of every single element versus the widget
# approach of normal windowing systems.
#
# Possible elements for display are handled via a data dictionary
# which can be modified by the controller and then processed via the
# various update functions.
#
# The available sections of the display are
# * Title:		The title of the particular display
#	*	Tasks: 		Overall tasks each with subtasks
#	*	Subtasks:	Individual operations within each task
#	*	Menus:		Application menu with subcommands
# * Content:	Main content area
# * Actions:	Operations to perform on the content; usually presented
#							in an sidebar
#
#	These can be presented via a XML tree with various attributes
# such as text, command ID, help description, and colors. These are
# presented internally in Python as dictionary entries.
class Lcars(gtk.DrawingArea):
		
	# -------------------------------------------------------------------
	def __init__(self):
		gtk.DrawingArea.__init__(self)
		
		self.Clear()
		
		self.font = pango.FontDescription('sans bold %s' % 14 )
		
		self.connect("expose_event", self.OnExpose)
		self.connect("button_press_event", self.OnPress)
		self.connect("button_release_event", self.OnRelease)
		self.connect("motion_notify_event", self.OnMotion)
		self.connect("configure_event", self.OnConfigure)
		
		self.set_events(gtk.gdk.EXPOSURE_MASK
			| gtk.gdk.BUTTON_PRESS_MASK
			| gtk.gdk.BUTTON_RELEASE_MASK
			| gtk.gdk.POINTER_MOTION_MASK
			| gtk.gdk.POINTER_MOTION_HINT_MASK)
	
	# -------------------------------------------------------------------
	def OnExpose(self, widget, event):
		wnd = self.window
		dw, dh = wnd.get_size()
		
		if dw <= 0 or dh <= 0:
			return
		
		ctx = wnd.cairo_create()
		ctx.set_source_rgb(0, 0, 0)
		ctx.scale( float(dw) / 1024, float(dh) / 768)
		ctx.rectangle(0, 0, 1024, 768)
		ctx.clip_preserve()
		ctx.fill()
		
		if self.mode == 'keyboard':
			self.DrawKeyboard(ctx)
		elif self.mode == 'login':
			ctx.set_source_rgb(.5, .5, 1)
			self.DrawTitleBar(ctx, 10, 10, 1004, 30, 'PossumPOS System 2008.01.23')
			self.DrawTitleBar(ctx, 10, 728, 1004, 30, 'Main Register at Brandon', pango.ALIGN_RIGHT)
		
		return False
		
	# -------------------------------------------------------------------
	def OnMotion(self, widget, event):
		if not self.indrag:
			return False
		
		if event.is_hint:
			x, y, state = event.window.get_pointer()
		else:
			x, y, state = event.x, event.y, event.state			
		return False
		
	# -------------------------------------------------------------------
	def OnPress(self, widget, event):
		if event.button == 1:
			self.indrag = True
			self.lastx, self.lasty = event.x, event.y
			
		return True
			
	# -------------------------------------------------------------------
	def OnRelease(self, widget, event):
		if event.button == 1:
			self.indrag = False
			
		return True
		
	# -------------------------------------------------------------------
	def OnConfigure(self, widget, event):
		return True
						
	# -------------------------------------------------------------------
	def OnTabClick(self, tab):
		return True
						
	# -------------------------------------------------------------------
	def Zoom(self, factor):
		self.magnification *= factor
		self.font = pango.FontDescription('sans bold %s' % int(12 * self.magnification) )
		self.queue_draw()
		
	# -------------------------------------------------------------------
	def Clear(self):
		self.title = None
		self.x, self.y = 0, 0
		self.lastx, self.lasty = 0, 0
		self.indrag = False
		
		self.mode	=	'login'
		self.states = {}
		self.buttons = {}
		
		self.tree = []
		
	# -------------------------------------------------------------------
	def SetMode(self, mode):
		self.mode = mode
		self.queue_draw()
		
	# -------------------------------------------------------------------
	def DrawImage(self, ctx, x, y, width, height, filename):
		pass
		
	# -------------------------------------------------------------------
	def DrawPillRight(self, ctx, x, y, radius):
		ctx.arc(x, y, radius, math.pi * 3 / 2, math.pi / 2)
		ctx.fill()
		
	# -------------------------------------------------------------------
	def DrawPillLeft(self, ctx, x, y, radius):
		ctx.arc(x, y, radius, math.pi / 2, math.pi * 3 / 2)
		ctx.fill()
		
	# -------------------------------------------------------------------
	def DrawTextBar(self, ctx, x, y, width, height, text, align = pango.ALIGN_LEFT):
		ctx.save()
		
		layout = ctx.create_layout()
		layout.set_font_description( pango.FontDescription('sans bold %s' % int(height * 0.7) ) )
		layout.set_text(text)
		lw, lh = layout.get_pixel_size()
		
		if align == pango.ALIGN_LEFT:
			ctx.move_to(x, y)
		elif align == pango.ALIGN_RIGHT:
			ctx.move_to(x + width - lw - 8, y)
		
		ctx.show_layout(layout)			
		
		if align == pango.ALIGN_LEFT:
			ctx.rectangle(x + lw + 4, y, width - lw - 12, height)
		elif align == pango.ALIGN_RIGHT:
			ctx.rectangle(x, y, width - lw - 12, height)
			
		ctx.fill()
		
		ctx.restore()
		
	# -------------------------------------------------------------------
	def DrawTitleBar(self, ctx, x, y, width, height, text, align = pango.ALIGN_LEFT):
		
		ctx.save()
		
		lg = cairo.LinearGradient(x, y, x, y + height)
		lg.add_color_stop_rgb(0, .3, .3, 1)
		lg.add_color_stop_rgb(.5, .8, .8, 1)
		lg.add_color_stop_rgb(1, .3, .3, 1)
		
		ctx.set_source(lg)
		
		self.DrawPillLeft(ctx, x + height / 2, y + height / 2, height / 2)
		self.DrawPillRight(ctx, x + width - height / 2, y + height / 2, height / 2)
		self.DrawTextBar(ctx, x + height / 2 + 4, y, width - height / 2 - 16, height, text, align)
		
		ctx.restore()
		
	# -------------------------------------------------------------------
	def DrawText(self, ctx, x, y, text):
		layout = ctx.create_layout()
		layout.set_font_description(self.font)
		lw, lh = layout.get_pixel_size()
		ctx.move_to(x, y)
		ctx.show_layout(layout)			
		
	# -------------------------------------------------------------------
	def DrawButton(self, ctx, x, y, width, height, text, action, textcolor = (0, 0, 0)):
		ctx.save()
		
		lg = cairo.LinearGradient(x, y, x, y + height)
		lg.add_color_stop_rgb(0, 0, .5, 0)
		lg.add_color_stop_rgb(.5, 0.2, 1, 0.2)
		lg.add_color_stop_rgb(1, 0, .5, 0)
		
		ctx.set_source(lg)
		
		radius = height / 2
		rectwidth = width - height / 2
		
		ctx.arc(x + radius, y + radius, radius, math.pi / 2, math.pi * 3 / 2)		
		ctx.line_to(x + radius + rectwidth, y)
		ctx.arc(x + width - radius, y + radius, radius, math.pi * 3 / 2, math.pi / 2)
		ctx.line_to(x + radius, y + height)
		ctx.fill()
		
		r, g, b = textcolor
		ctx.set_source_rgb(r, g, b)
		
		layout = ctx.create_layout()
		
		textsize = int(height * 0.5)
		layout.set_text(text)
		
		layout.set_font_description( pango.FontDescription('sans bold %s' % textsize) )
		lw, lh = layout.get_pixel_size()
		
		textratio = float(lw) / lh
		buttonratio = float(width) / height
		
		if textratio > buttonratio:
			layout.set_font_description( 
				pango.FontDescription('sans bold %s' % int(textsize * buttonratio / textratio) ) 
			)
			lw, lh = layout.get_pixel_size()
		
		ctx.move_to( x + width / 2 - lw / 2, y + height / 2 - lh / 2)		
		ctx.show_layout(layout)			
		
		self.buttons[text] = possumlib.geometry.Rect(x, y, width, height)
		
		ctx.restore()
		
	# -------------------------------------------------------------------
	def DrawKeyboard(self, ctx):
		ctx.save()
		modifiers = [ 'Symbols', 'Caps', 'Shift',]
		
		for i in range(0, len(modifiers)):
			self.DrawButton(ctx, 4, 460 + (i * 64), 80, 60, modifiers[i], modifiers[i])
			
		rows = ['1234567890', 'qwertyuiop', 'asdfghjkl;', 'zxcvbnm,./']
		
		rowpos = 0
		for r in rows:
			y = 400 + rowpos * 64
			for i in range(0, len(r)):
				self.DrawButton(ctx, 88 + rowpos * 12 + (i * 78), y, 74, 60, r[i], r[i])
				
			rowpos += 1
			
		self.DrawButton(ctx, 88, 404 + rowpos * 64, 800, 60, 'Space', 'Space')
		
		entries = ['Backspace', 'Enter', 'Submit', 'Cancel']
		
		for i in range(0, len(entries)):
			self.DrawButton(ctx, 908, 400 + (i * 64), 108, 60, entries[i], entries[i])
			
		ctx.restore()
		
		for k, v in self.buttons.iteritems():
			print	'%s\t%s' % (k, v)

# *********************************************************************
gobject.signal_new("clicked", 
	Lcars,
	gobject.SIGNAL_ACTION,
	gobject.TYPE_NONE,
	(gobject.TYPE_PYOBJECT,) )
	
